<?php
/**
 *  💡 免责声明：本软件不得用于商业用途，仅做学习交流
 *  ⚠️ 权利声明：本软件由版权人提供产品技术支持
 *  📅 项目编号：NX2025-0135
 */
namespace App\Utils;

use Illuminate\Support\Facades\Redis;

class OrderNumberGenerator
{
    // 使用组合模式生成唯一ID
    public static function generate($businessId = 0)
    {
        // 1. 基础时间部分（精确到毫秒）
        $microtime = microtime(true);
        $timestamp = (int) ($microtime * 1000);
        $datePrefix = date('YmdHis', $microtime);

        // 2. 机器标识部分（解决分布式问题）
        $machineId = self::getMachineId();

        // 3. 业务标识部分
        $businessPrefix = str_pad($businessId % 100, 2, '0', STR_PAD_LEFT);

        // 4. 序列号部分（使用Redis原子操作）
        $sequence = self::getRedisSequence();

        // 5. 组合各部分
        $baseNumber = $datePrefix . $businessPrefix . $machineId;

        // 6. 拼接完整订单号
        $orderNumber = sprintf(
            '%s%04d%04d',
            $baseNumber,
            $timestamp % 10000,
            $sequence % 10000
        );

        // 7. 加入校验码
        $orderNumber .= self::generateChecksum($orderNumber);

        return $orderNumber;
    }

    private static function getMachineId()
    {
        // 从服务器环境变量获取（Docker/K8s环境）
        if ($dockerHostname = getenv('HOSTNAME')) {
            return abs(crc32($dockerHostname)) % 100;
        }

        // 物理服务器取IP末段
        $ip = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1';
        $ipParts = explode('.', $ip);
        return end($ipParts) % 100;
    }

    private static function getRedisSequence()
    {
        try {
            $key = 'order:seq:' . date('YmdH');
            $seq = Redis::incr($key);
            Redis::expire($key, 3600);
            return $seq % 10000;
        } catch (Exception $e) {
            // 降级方案：使用文件锁+计数器
            return self::getFileBasedSequence();
        }
    }

    private static function getFileBasedSequence()
    {
        $lockFile = sys_get_temp_dir() . '/order_seq.lock';
        $fp = fopen($lockFile, 'w+');

        if (flock($fp, LOCK_EX)) {
            $counterFile = sys_get_temp_dir() . '/order_seq.counter';
            $count = file_exists($counterFile) ? (int) file_get_contents($counterFile) : 0;
            $count = ($count + 1) % 10000;
            file_put_contents($counterFile, $count);
            flock($fp, LOCK_UN);
        }

        fclose($fp);
        return $count;
    }

    private static function generateChecksum($number)
    {
        // 改进版Luhn算法校验码
        $sum = 0;
        $alt = false;

        for ($i = strlen($number) - 1; $i >= 0; --$i) {
            $n = (int) $number[$i];
            if ($alt) {
                $n *= 2;
                $sum += ($n > 9) ? ($n - 9) : $n;
            } else {
                $sum += $n;
            }
            $alt = ! $alt;
        }

        return (10 - ($sum % 10)) % 10;
    }
}
